TypeScriptã®äŸåæ§æ³šå ¥ãIoCã³ã³ãããããã³éèŠãªåå®å šæŠç¥ãæ¢æ±ããã°ããŒãã«ãªéçºç°å¢ã§ä¿å®æ§ããã¹ãå®¹ææ§ãå ç¢æ§ã«åªããã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ããŸãããã¹ããã©ã¯ãã£ã¹ãšå®è·µäŸãæ·±ãæãäžããŸãã
TypeScriptã®äŸåæ§æ³šå ¥ïŒå ç¢ãªã°ããŒãã«ã¢ããªã±ãŒã·ã§ã³ã®ããã®IoCã³ã³ããã®åå®å šæ§ã®åŒ·å
çŸä»£ã®ãœãããŠã§ã¢éçºãçžäºã«é£çµãããäžçã«ãããŠãä¿å®æ§ãã¹ã±ãŒã©ããªãã£ããã¹ãå®¹ææ§ã«åªããã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ããããšã¯æãéèŠã§ããããŒã ã忣ãããããžã§ã¯ãããŸããŸãè€éã«ãªãã«ã€ããŠãé©åã«æ§é åãããççµåãªã³ãŒãã®å¿ èŠæ§ãé«ãŸããŸããäŸåæ§æ³šå ¥ïŒDIïŒãšå¶åŸ¡ã®å転ïŒIoCïŒã³ã³ããã¯ããããã®èª²é¡ã«æ£é¢ããåãçµã匷åãªã¢ãŒããã¯ãã£ãã¿ãŒã³ã§ããTypeScriptã®éçåä»ãæ©èœãšçµã¿åãããããšã§ããããã®ãã¿ãŒã³ã¯äºæž¬å¯èœæ§ãšå ç¢æ§ã®æ°ããã¬ãã«ãè§£ãæŸã¡ãŸãããã®å æ¬çãªã¬ã€ãã§ã¯ãTypeScriptã®äŸåæ§æ³šå ¥ãIoCã³ã³ããã®åœ¹å²ããããŠäœããããå ç¢ãªåå®å šæ§ãéæããæ¹æ³ãæ·±ãæãäžããã°ããŒãã«ãªã¢ããªã±ãŒã·ã§ã³ãéçºãšå€æŽã®å³ããã®äžã§åŒ·åã«æ©èœãç¶ããããšãä¿èšŒããŸãã
åºç€ïŒäŸåæ§æ³šå ¥ãçè§£ãã
IoCã³ã³ãããšåå®å šæ§ãæ¢æ±ããåã«ãäŸåæ§æ³šå ¥ã®æŠå¿µããã£ãããšçè§£ããŸããããDIã®æ¬è³ªã¯ãå¶åŸ¡ã®å転ã®ååãå®è£ ãããã¶ã€ã³ãã¿ãŒã³ã§ããã³ã³ããŒãã³ããèªèº«ã®äŸåæ§ãäœæãã代ããã«ãå€éšãœãŒã¹ããããããåãåããŸãããã®ãæ³šå ¥ãã¯ããã€ãã®æ¹æ³ã§çºçããŸãïŒ
- ã³ã³ã¹ãã©ã¯ã¿ã€ã³ãžã§ã¯ã·ã§ã³ïŒäŸåæ§ã¯ã³ã³ããŒãã³ãã®ã³ã³ã¹ãã©ã¯ã¿ã®åŒæ°ãšããŠæäŸãããŸããããã¯ãã³ã³ããŒãã³ããåžžã«å¿ èŠãªãã¹ãŠã®äŸåæ§ãšãšãã«åæåãããããšãä¿èšŒãããã®èŠä»¶ãæç€ºçã«ããããããã°ãã°æšå¥šãããæ¹æ³ã§ãã
- ã»ãã¿ãŒã€ã³ãžã§ã¯ã·ã§ã³ïŒããããã£ã€ã³ãžã§ã¯ã·ã§ã³ïŒïŒäŸåæ§ã¯ãã³ã³ããŒãã³ããæ§ç¯ãããåŸã«ãããªãã¯ã»ãã¿ãŒã¡ãœãããŸãã¯ããããã£ãéããŠæäŸãããŸããããã¯æè»æ§ãæäŸããŸãããäŸåæ§ãèšå®ãããŠããªãå Žåãã³ã³ããŒãã³ããäžå®å šãªç¶æ ã«ãªãå¯èœæ§ããããŸãã
- ã¡ãœããã€ã³ãžã§ã¯ã·ã§ã³ïŒäŸåæ§ã¯ãããããå¿ èŠãšããç¹å®ã®ã¡ãœããã«æäŸãããŸããããã¯ãã³ã³ããŒãã³ãã®ã©ã€ããµã€ã¯ã«å šäœã§ã¯ãªããç¹å®ã®æäœã«ã®ã¿å¿ èŠãªäŸåæ§ã«é©ããŠããŸãã
ãªãäŸåæ§æ³šå ¥ãåãå ¥ããã®ãïŒã°ããŒãã«ãªå©ç¹
éçºããŒã ã®èŠæš¡ãå°ççååžã«ããããããäŸåæ§æ³šå ¥ã®å©ç¹ã¯æ®éçã«èªèãããŠããŸãïŒ
- ãã¹ãå®¹ææ§ã®åäžïŒDIã䜿çšãããšãã³ã³ããŒãã³ãã¯ç¬èªã®äŸåæ§ãäœæããŸãããããã¯ããã¹ãäžã«äŸåæ§ã®ã¢ãã¯ããŒãžã§ã³ãã¹ã¿ãããŒãžã§ã³ãç°¡åã«ãæ³šå ¥ãã§ããããšãæå³ããååè ããã®å¯äœçšãªãã«åäžã®ã³ãŒããŠããããåé¢ããŠãã¹ãã§ããŸããããã¯ãããããéçºç°å¢ã§ã®è¿ éãã€ä¿¡é Œæ§ã®é«ããã¹ãã«ãšã£ãŠäžå¯æ¬ ã§ãã
- ä¿å®æ§ã®åäžïŒççµåãªã³ã³ããŒãã³ãã¯ãçè§£ã倿Žãæ¡åŒµã容æã§ããããäŸåæ§ã®å€æŽãã¢ããªã±ãŒã·ã§ã³ã®ç¡é¢ä¿ãªéšåã«æ³¢åããå¯èœæ§ãäœããªãã倿§ãªã³ãŒãããŒã¹ãšããŒã éã§ã®ä¿å®ãç°¡çŽ åãããŸãã
- æè»æ§ãšåå©çšæ§ã®åäžïŒã³ã³ããŒãã³ãã¯ããã¢ãžã¥ãŒã«åãããç¬ç«æ§ãé«ãŸããŸããäŸåæ§ã®å®è£ ããããã䜿çšããã³ã³ããŒãã³ãã倿Žããããšãªã亀æã§ããç°ãªããããžã§ã¯ããç°å¢éã§ã®ã³ãŒãã®åå©çšãä¿é²ããŸããäŸãã°ãéçºç°å¢ã§ã¯`SQLiteDatabaseService`ãæ³šå ¥ããæ¬çªç°å¢ã§ã¯`PostgreSQLDatabaseService`ãæ³šå ¥ããããšãã§ããŸããã`UserService`ã倿Žããå¿ èŠã¯ãããŸããã
- å®åã³ãŒãã®åæžïŒæåã¯çŽæã«åããããã«èŠãããããããŸããããç¹ã«æåDIã®å ŽåãIoCã³ã³ããïŒæ¬¡ã«èª¬æããŸãïŒã¯ãäŸåæ§ãæåã§æ¥ç¶ããããšã«äŒŽãå®åã³ãŒããå€§å¹ ã«åæžã§ããŸãã
- ããæç¢ºãªèšèšãšæ§é ïŒDIã¯ãéçºè ã«ã³ã³ããŒãã³ãã®è²¬ä»»ãšãã®å€éšèŠä»¶ã«ã€ããŠèãããããããã°ããŒãã«ããŒã ãçè§£ããããå ±åäœæ¥ãããããããã¯ãªãŒã³ã§çŠç¹ã®å®ãŸã£ãã³ãŒãã«ã€ãªãããŸãã
IoCã³ã³ããã䜿çšããªããã³ã³ã¹ãã©ã¯ã¿ã€ã³ãžã§ã¯ã·ã§ã³ã瀺ãã·ã³ãã«ãªTypeScriptã®äŸãèããŠã¿ãŸãããïŒ
interface ILogger {
log(message: string): void;
}
class ConsoleLogger implements ILogger {
log(message: string): void {
console.log(`[LOG]: ${message}`);
}
}
class DataService {
private logger: ILogger;
constructor(logger: ILogger) {
this.logger = logger;
}
fetchData(): string {
this.logger.log("Fetching data...");
// ... data fetching logic ...
return "Some important data";
}
}
// Manual Dependency Injection
const myLogger: ILogger = new ConsoleLogger();
const myDataService = new DataService(myLogger);
console.log(myDataService.fetchData());
ãã®äŸã§ã¯ã`DataService`ã¯`ConsoleLogger`èªäœãäœæããŸãããã³ã³ã¹ãã©ã¯ã¿ãä»ããŠ`ILogger`ã®ã€ã³ã¹ã¿ã³ã¹ãåãåããŸããããã«ããã`DataService`ã¯å ·äœçãª`ILogger`ã®å®è£ ã«äŸåããã容æãªçœ®ãæããå¯èœã«ãªããŸãã
ãªãŒã±ã¹ãã¬ãŒã¿ãŒïŒå¶åŸ¡ã®å転ïŒIoCïŒã³ã³ãã
æåã®äŸåæ§æ³šå ¥ã¯å°èŠæš¡ãªã¢ããªã±ãŒã·ã§ã³ã§ã¯å¯èœã§ãããããå€§èŠæš¡ãªãšã³ã¿ãŒãã©ã€ãºã°ã¬ãŒãã®ã·ã¹ãã ã§ãªããžã§ã¯ãã®äœæãšäŸåæ§ã°ã©ãã管çããããšã¯ãããã«ç ©éã«ãªãå¯èœæ§ããããŸããããã§å¶åŸ¡ã®å転ïŒIoCïŒã³ã³ãããå¥åDIã³ã³ãããç»å ŽããŸããIoCã³ã³ããã¯ããªããžã§ã¯ããšãã®äŸåæ§ã®ã€ã³ã¹ã¿ã³ã¹åãšã©ã€ããµã€ã¯ã«ã管çãããã¬ãŒã ã¯ãŒã¯ã§ãã
IoCã³ã³ããã®ä»çµã¿
IoCã³ã³ããã¯éåžžã2ã€ã®äž»èŠãªãã§ãŒãºã§åäœããŸãïŒ
-
ç»é²ïŒãã€ã³ãã£ã³ã°ïŒïŒã³ã³ããã«ã¢ããªã±ãŒã·ã§ã³ã®ã³ã³ããŒãã³ããšãã®é¢ä¿ããæããŸãããããã«ã¯ãæœè±¡ã€ã³ã¿ãŒãã§ãŒã¹ãŸãã¯ããŒã¯ã³ãå ·äœçãªå®è£ ã«ãããã³ã°ããããšãå«ãŸããŸããäŸãã°ãã³ã³ããã«ã誰ãã`ILogger`ãèŠæ±ãããã³ã«ã`ConsoleLogger`ã€ã³ã¹ã¿ã³ã¹ãæäŸããŠãã ããããšäŒããŸãã
// Conceptual registration container.bind<ILogger>("ILogger").to(ConsoleLogger); -
解決ïŒã€ã³ãžã§ã¯ã·ã§ã³ïŒïŒã³ã³ããŒãã³ããäŸåæ§ãå¿ èŠãšãããšããã³ã³ããã«ãããæäŸããããã«äŸé ŒããŸããã³ã³ããã¯ã³ã³ããŒãã³ãã®ã³ã³ã¹ãã©ã¯ã¿ïŒãŸãã¯DIã¹ã¿ã€ã«ã«å¿ããŠããããã£/ã¡ãœããïŒãæ€æ»ãããã®äŸåæ§ãèå¥ãããããã®äŸåæ§ã®ã€ã³ã¹ã¿ã³ã¹ãäœæãïŒããããããã«ç¬èªã®äŸåæ§ãæã£ãŠããå Žåã¯ååž°çã«è§£æ±ºãïŒããã®åŸãèŠæ±ãããã³ã³ããŒãã³ãã«ããããæ³šå ¥ããŸãããã®ããã»ã¹ã¯ãã¢ãããŒã·ã§ã³ãŸãã¯ãã³ã¬ãŒã¿ãä»ããŠèªååãããããšããããããŸãã
// Conceptual resolution const dataService = container.resolve<DataService>(DataService);
ã³ã³ããã¯ãªããžã§ã¯ãã®ã©ã€ããµã€ã¯ã«ç®¡çã®è²¬ä»»ãè² ããã¢ããªã±ãŒã·ã§ã³ã³ãŒããããã¯ãªãŒã³ã«ããã€ã³ãã©ã¹ãã©ã¯ãã£ã®æžå¿µäºé ã§ã¯ãªãããžãã¹ããžãã¯ã«éäžãããŸãããã®é¢å¿ã®åé¢ã¯ãå€§èŠæš¡éçºã忣ããŒã ã«ãšã£ãŠéåžžã«äŸ¡å€ããããŸãã
TypeScriptã®å©ç¹ïŒéçåä»ããšãã®DI課é¡
TypeScriptã¯JavaScriptã«éçåä»ãããããããéçºè ãå®è¡æã§ã¯ãªãéçºã®æ©ã段éã§ãšã©ãŒãææã§ããããã«ããŸãããã®ã³ã³ãã€ã«æã®å®å šæ§ã¯ã倿§ãªã°ããŒãã«ããŒã ã«ãã£ãŠä¿å®ãããè€éãªã·ã¹ãã ã«ãšã£ãŠç¹ã«å€§ããªå©ç¹ã§ãããã³ãŒãå質ãåäžããããããã°æéãåæžããŸãã
ããããå®è¡æãªãã¬ã¯ã·ã§ã³ãæååããŒã¹ã®ã«ãã¯ã¢ããã«å€§ããäŸåããåŸæ¥ã®JavaScript DIã³ã³ããã¯ãTypeScriptã®éçãªæ§è³ªãšè¡çªããããšããããŸãããã®çç±ã¯æ¬¡ã®ãšããã§ãïŒ
- å®è¡æ vs. ã³ã³ãã€ã«æïŒTypeScriptã®åã¯äž»ã«ã³ã³ãã€ã«æã®æ§é äœã§ãããããã¯ããã¬ãŒã³ãªJavaScriptãžã®ã³ã³ãã€ã«äžã«æ¶å»ãããŸããããã¯ãå®è¡æã«JavaScriptãšã³ãžã³ãTypeScriptã®ã€ã³ã¿ãŒãã§ãŒã¹ãåã¢ãããŒã·ã§ã³ã«ã€ããŠæ¬è³ªçã«ç¥ããªãããšãæå³ããŸãã
- åæ å ±ã®æå€±ïŒDIã³ã³ãããå®è¡æã«JavaScriptã³ãŒããåçã«æ€æ»ããïŒäŸïŒé¢æ°åŒæ°ãè§£æããããæååããŒã¯ã³ã«äŸåãããããïŒå ŽåãTypeScriptãæäŸããè±å¯ãªåæ å ±ã倱ãããå¯èœæ§ããããŸãã
- ãªãã¡ã¯ã¿ãªã³ã°ã®ãªã¹ã¯ïŒäŸåæ§èå¥åãšããŠæååãªãã©ã«ãããŒã¯ã³ãã䜿çšããå Žåãã¯ã©ã¹åãã€ã³ã¿ãŒãã§ãŒã¹åããªãã¡ã¯ã¿ãªã³ã°ããŠããDIèšå®ã§ã³ã³ãã€ã«æãšã©ãŒãçºçããªãå¯èœæ§ããããå®è¡æãšã©ãŒã«ã€ãªãããŸããããã¯ãå€§èŠæš¡ã§é²åããã³ãŒãããŒã¹ã«ãããŠé倧ãªãªã¹ã¯ã§ãã
ãããã£ãŠã課é¡ã¯ãTypeScriptã®IoCã³ã³ãããããã®éçåæ å ±ãä¿æããã³å©çšããã³ã³ãã€ã«æã®å®å šæ§ã確ä¿ããäŸåæ§è§£æ±ºã«é¢é£ããå®è¡æãšã©ãŒãé²ãæ¹æ³ã§æŽ»çšããããšã§ãã
TypeScriptã®IoCã³ã³ããã§åå®å šæ§ãéæãã
ç®æšã¯ãã³ã³ããŒãã³ãã`ILogger`ãæåŸ ããå ŽåãIoCã³ã³ãããåžžã«`ILogger`ã«æºæ ããã€ã³ã¹ã¿ã³ã¹ãæäŸããTypeScriptããããã³ã³ãã€ã«æã«æ€èšŒã§ããããã«ããããšã§ããããã«ããã`UserService`ã誀ã£ãŠ`PaymentProcessor`ã€ã³ã¹ã¿ã³ã¹ãåãåã£ãŠããŸãã·ããªãªãé²ããçºèŠãã«ããå®è¡æåé¡ã«ã€ãªããããšãåé¿ããŸãã
çŸä»£ã®TypeScriptãã¡ãŒã¹ãã®IoCã³ã³ããã¯ããã®éèŠãªåå®å šæ§ãéæããããã«ããã€ãã®æŠç¥ãšãã¿ãŒã³ãæ¡çšããŠããŸãïŒ
1. æœè±¡åã®ããã®ã€ã³ã¿ãŒãã§ãŒã¹
ããã¯TypeScriptã ãã§ãªããåªããDIèšèšã®åºæ¬ã§ããåžžã«å ·äœçãªå®è£ ã§ã¯ãªããæœè±¡åïŒã€ã³ã¿ãŒãã§ãŒã¹ïŒã«äŸåããŠãã ãããTypeScriptã®ã€ã³ã¿ãŒãã§ãŒã¹ã¯ãã¯ã©ã¹ãéµå®ããªããã°ãªããªãå¥çŽãæäŸããäŸåæ§åãå®çŸ©ããã®ã«åªããŠããŸãã
// Define the contract
interface IEmailService {
sendEmail(to: string, subject: string, body: string): Promise<void>;
}
// Concrete implementation 1
class SmtpEmailService implements IEmailService {
async sendEmail(to: string, subject: string, body: string): Promise<void> {
console.log(`Sending SMTP email to ${to}: ${subject}`);
// ... actual SMTP logic ...
}
}
// Concrete implementation 2 (e.g., for testing or different provider)
class MockEmailService implements IEmailService {
async sendEmail(to: string, subject: string, body: string): Promise<void> {
console.log(`[MOCK] Sending email to ${to}: ${subject}`);
// No actual sending, just for testing or development
}
}
class NotificationService {
constructor(private emailService: IEmailService) {}
async notifyUser(userId: string, message: string): Promise<void> {
// Imagine retrieving user email here
const userEmail = "user@example.com";
await this.emailService.sendEmail(userEmail, "Notification", message);
}
}
ããã§ã¯ã`NotificationService`ã¯`SmtpEmailService`ã§ã¯ãªã`IEmailService`ã«äŸåããŠããŸããããã«ãããå®è£ ãç°¡åã«äº€æã§ããŸãã
2. ã€ã³ãžã§ã¯ã·ã§ã³ããŒã¯ã³ïŒã·ã³ãã«ãŸãã¯åã¬ãŒãä»ãæååãªãã©ã«ïŒ
TypeScriptã®ã€ã³ã¿ãŒãã§ãŒã¹ã¯å®è¡æã«æ¶å»ããããããIoCã³ã³ããã§ã®äŸåæ§è§£æ±ºã®ããŒãšããŠã€ã³ã¿ãŒãã§ãŒã¹ãçŽæ¥äœ¿çšããããšã¯ã§ããŸãããäŸåæ§ãäžæã«èå¥ããå®è¡æãããŒã¯ã³ããå¿ èŠã§ãã
-
æååãªãã©ã«ïŒã·ã³ãã«ã§ããããªãã¡ã¯ã¿ãªã³ã°ãšã©ãŒãçºçããããã§ããæååã倿ŽããŠããTypeScriptã¯èŠåããŸããã
// container.bind<IEmailService>("EmailService").to(SmtpEmailService); // container.get<IEmailService>("EmailService"); -
ã·ã³ãã«ïŒæååã«ä»£ããããå®å šãªæ¹æ³ã§ããã·ã³ãã«ã¯äžæã§ãããè¡çªããããšã¯ãããŸããããããã¯å®è¡æã®å€ã§ãããåãšé¢é£ä»ããããšãã§ããŸãã
// Define a unique Symbol as an injection token const TYPES = { EmailService: Symbol.for("IEmailService"), NotificationService: Symbol.for("NotificationService"), }; // Example with InversifyJS (a popular TypeScript IoC container) import { Container, injectable, inject } from "inversify"; import "reflect-metadata"; // Required for decorators interface IEmailService { sendEmail(to: string, subject: string, body: string): Promise<void>; } @injectable() class SmtpEmailService implements IEmailService { async sendEmail(to: string, subject: string, body: string): Promise<void> { console.log(`Sending SMTP email to ${to}: ${subject}`); } } @injectable() class NotificationService { constructor( @inject(TYPES.EmailService) private emailService: IEmailService ) {} async notifyUser(userId: string, message: string): Promise<void> { const userEmail = "user@example.com"; await this.emailService.sendEmail(userEmail, "Notification", message); } } const container = new Container(); container.bind<IEmailService>(TYPES.EmailService).to(SmtpEmailService); container.bind<NotificationService>(TYPES.NotificationService).to(NotificationService); const notificationService = container.get<NotificationService>(TYPES.NotificationService); notificationService.notifyUser("123", "Hello, world!");ã¿ã€ããªããžã§ã¯ããš`Symbol.for`ã䜿çšããããšã¯ãããŒã¯ã³ã管çããããã®å ç¢ãªæ¹æ³ãæäŸããŸããTypeScriptã¯ã`bind`ããã³`get`åŒã³åºãã§`<IEmailService>`ã䜿çšããå Žåã§ãåãã§ãã¯ãæäŸããŸãã
3. ãã³ã¬ãŒã¿ãš`reflect-metadata`
IoCã³ã³ãããšçµã¿åãããTypeScriptãç䟡ãçºæ®ããã®ã¯ããã§ããJavaScriptã®`reflect-metadata` APIïŒå€ãç°å¢ãç¹å®ã®TypeScriptèšå®ã§ã¯ããªãã£ã«ãå¿ èŠã§ãïŒã¯ãéçºè ãã¯ã©ã¹ãã¡ãœãããããããã£ã«ã¡ã¿ããŒã¿ãã¢ã¿ããããããšãå¯èœã«ããŸããTypeScriptã®å®éšçãªãã³ã¬ãŒã¿ã¯ãã®æ©èœã掻çšããIoCã³ã³ãããèšèšæã«ã³ã³ã¹ãã©ã¯ã¿ãã©ã¡ãŒã¿ãæ€æ»ã§ããããã«ããŸãã
`tsconfig.json`ã§`emitDecoratorMetadata`ãæå¹ã«ãããšãTypeScriptã¯ã¯ã©ã¹ã³ã³ã¹ãã©ã¯ã¿ã®ãã©ã¡ãŒã¿ã®åã«é¢ãã远å ã®ã¡ã¿ããŒã¿ãåºåããŸããIoCã³ã³ããã¯ããã®ã¡ã¿ããŒã¿ãå®è¡æã«èªã¿åããäŸåæ§ãèªåçã«è§£æ±ºã§ããŸããããã¯ãåæ å ±ãå©çšå¯èœã§ãããããå ·äœçãªã¯ã©ã¹ã®ããŒã¯ã³ãæç€ºçã«æå®ããå¿ èŠããªãããšãå€ãããšãæå³ããŸãã
// tsconfig.json excerpt:
// {
// "compilerOptions": {
// "experimentalDecorators": true,
// "emitDecoratorMetadata": true
// }
// }
import { Container, injectable, inject } from "inversify";
import "reflect-metadata"; // Essential for decorator metadata
// --- Dependencies ---
interface IDataRepository {
findById(id: string): Promise<any>;
}
@injectable()
class MongoDataRepository implements IDataRepository {
async findById(id: string): Promise<any> {
console.log(`Fetching data from MongoDB for ID: ${id}`);
return { id, name: "MongoDB User" };
}
}
interface ILogger {
log(message: string): void;
}
@injectable()
class ConsoleLogger implements ILogger {
log(message: string): void {
console.log(`[App Logger]: ${message}`);
}
}
// --- Service requiring dependencies ---
@injectable()
class UserService {
constructor(
@inject(TYPES.DataRepository) private dataRepository: IDataRepository,
@inject(TYPES.Logger) private logger: ILogger
) {
this.logger.log("UserService initialized.");
}
async getUser(id: string): Promise<any> {
this.logger.log(`Attempting to get user with ID: ${id}`);
const user = await this.dataRepository.findById(id);
this.logger.log(`User ${user.name} retrieved.`);
return user;
}
}
// --- IoC Container Setup ---
const TYPES = {
DataRepository: Symbol.for("IDataRepository"),
Logger: Symbol.for("ILogger"),
UserService: Symbol.for("UserService"),
};
const appContainer = new Container();
// Bind interfaces to concrete implementations using symbols
appContainer.bind<IDataRepository>(TYPES.DataRepository).to(MongoDataRepository);
appContainer.bind<ILogger>(TYPES.Logger).to(ConsoleLogger);
// Bind the concrete class for UserService
// The container will automatically resolve its dependencies based on @inject decorators and reflect-metadata
appContainer.bind<UserService>(TYPES.UserService).to(UserService);
// --- Application Execution ---
const userService = appContainer.get<UserService>(TYPES.UserService);
userService.getUser("user-123").then(user => {
console.log("User fetched successfully:", user);
});
ãã®æ¡åŒµãããäŸã§ã¯ã`reflect-metadata`ãš`@inject`ãã³ã¬ãŒã¿ã«ããã`InversifyJS`ã¯`UserService`ã`IDataRepository`ãš`ILogger`ãå¿ èŠãšããããšãèªåçã«çè§£ããŸãã`bind`ã¡ãœããã®åãã©ã¡ãŒã¿`<IDataRepository>`ã¯ã`MongoDataRepository`ãå®éã«`IDataRepository`ãå®è£ ããŠããããšãä¿èšŒããã³ã³ãã€ã«æãã§ãã¯ãæäŸããŸãã
ãã`IDataRepository`ãå®è£ ããªãã¯ã©ã¹ã誀ã£ãŠ`TYPES.DataRepository`ã«ãã€ã³ãããå ŽåãTypeScriptã¯ã³ã³ãã€ã«æãšã©ãŒãçºè¡ããæœåšçãªå®è¡æã¯ã©ãã·ã¥ãé²ããŸãããããTypeScriptã®IoCã³ã³ããã«ãããåå®å šæ§ã®æ¬è³ªã§ããã€ãŸãããšã©ãŒããŠãŒã¶ãŒã«å°éããåã«ææããããšã§ãå°ççã«åæ£ããéçºããŒã ãããã·ã§ã³ã¯ãªãã£ã«ã«ãªã·ã¹ãã ã§äœæ¥ããäžã§å€§ããªå©ç¹ãšãªããŸãã
äžè¬çãªTypeScript IoCã³ã³ãããæ·±ãæãäžãã
ååã¯äžè²«ããŠããŸãããç°ãªãIoCã³ã³ããã¯ããŸããŸãªæ©èœãšAPIã¹ã¿ã€ã«ãæäŸããŸããTypeScriptã®åå®å šæ§ãæ¡çšããŠããããã€ãã®äººæ°ã®ããéžæè¢ãèŠãŠã¿ãŸãããã
InversifyJS
InversifyJSã¯ãTypeScriptã§æãæçããåºãæ¡çšãããŠããIoCã³ã³ããã®1ã€ã§ããTypeScriptã®æ©èœãç¹ã«ãã³ã¬ãŒã¿ãš`reflect-metadata`ãæŽ»çšããããã«ãŒãããæ§ç¯ãããŠããŸãããã®èšèšã¯ãåå®å šæ§ãç¶æããããã«ã€ã³ã¿ãŒãã§ãŒã¹ãšã·ã³ããªãã¯ã€ã³ãžã§ã¯ã·ã§ã³ããŒã¯ã³ã匷ã匷調ããŠããŸãã
äž»èŠæ©èœïŒ
- ãã³ã¬ãŒã¿ããŒã¹ïŒ`@injectable()`ã`@inject()`ã`@multiInject()`ã`@named()`ã`@tagged()`ã䜿çšããæç¢ºã§å®£èšçãªäŸåæ§ç®¡çãå®çŸããŸãã
- ã·ã³ããªãã¯èå¥åïŒã€ã³ãžã§ã¯ã·ã§ã³ããŒã¯ã³ã«Symbolsã䜿çšããããšãæšå¥šããŠãããããã¯ã°ããŒãã«ã«äžæã§ãããæååã«æ¯ã¹ãŠååã®è¡çªãæžãããŸãã
- ã³ã³ããã¢ãžã¥ãŒã«ã·ã¹ãã ïŒç¹ã«å€§èŠæš¡ãããžã§ã¯ãã«ãããŠãããè¯ãã¢ããªã±ãŒã·ã§ã³æ§é ã®ããã«ãã€ã³ãã£ã³ã°ãã¢ãžã¥ãŒã«ã«æŽçã§ããŸãã
- ã©ã€ããµã€ã¯ã«ã¹ã³ãŒãïŒãã©ã³ãžã§ã³ãïŒãªã¯ãšã¹ãããšã«æ°ããã€ã³ã¹ã¿ã³ã¹ïŒãã·ã³ã°ã«ãã³ïŒã³ã³ããããšã«åäžã€ã³ã¹ã¿ã³ã¹ïŒãããã³ãªã¯ãšã¹ã/ã³ã³ããã¹ã³ãŒãã®ãã€ã³ãã£ã³ã°ããµããŒãããŸãã
- æ¡ä»¶ä»ããã€ã³ãã£ã³ã°ïŒã³ã³ããã¹ãã«ãŒã«ã«åºã¥ããŠç°ãªãå®è£ ããã€ã³ãã§ããŸãïŒäŸïŒéçºç°å¢ã®å Žåã«`DevelopmentLogger`ããã€ã³ããããªã©ïŒã
- éåæè§£æ±ºïŒéåæã§è§£æ±ºããå¿ èŠãããäŸåæ§ãåŠçã§ããŸãã
InversifyJSã®äŸïŒæ¡ä»¶ä»ããã€ã³ãã£ã³ã°
ã¢ããªã±ãŒã·ã§ã³ããŠãŒã¶ãŒã®å°åãç¹å®ã®ããžãã¹ããžãã¯ã«åºã¥ããŠç°ãªãæ¯æãããã»ããµãå¿ èŠãšããç¶æ³ãæ³åããŠã¿ãŠãã ãããInversifyJSã¯ãæ¡ä»¶ä»ããã€ã³ãã£ã³ã°ã§ããããšã¬ã¬ã³ãã«åŠçããŸãã
import { Container, injectable, inject, interfaces } from "inversify";
import "reflect-metadata";
const APP_TYPES = {
PaymentProcessor: Symbol.for("IPaymentProcessor"),
OrderService: Symbol.for("IOrderService"),
};
interface IPaymentProcessor {
processPayment(amount: number): Promise<boolean>;
}
@injectable()
class StripePaymentProcessor implements IPaymentProcessor {
async processPayment(amount: number): Promise<boolean> {
console.log(`Processing ${amount} with Stripe...`);
return true;
}
}
@injectable()
class PayPalPaymentProcessor implements IPaymentProcessor {
async processPayment(amount: number): Promise<boolean> {
console.log(`Processing ${amount} with PayPal...`);
return true;
}
}
@injectable()
class OrderService {
constructor(
@inject(APP_TYPES.PaymentProcessor) private paymentProcessor: IPaymentProcessor
) {}
async placeOrder(orderId: string, amount: number, paymentMethod: 'stripe' | 'paypal'): Promise<boolean> {
console.log(`Placing order ${orderId} for ${amount}...`);
const success = await this.paymentProcessor.processPayment(amount);
if (success) {
console.log(`Order ${orderId} placed successfully.`);
} else {
console.log(`Order ${orderId} failed.`);
}
return success;
}
}
const container = new Container();
// Bind Stripe as default
container.bind<IPaymentProcessor>(APP_TYPES.PaymentProcessor).to(StripePaymentProcessor);
// Conditionally bind PayPal if the context requires it (e.g., based on a tag)
container.bind<IPaymentProcessor>(APP_TYPES.PaymentProcessor)
.to(PayPalPaymentProcessor)
.whenTargetTagged("paymentMethod", "paypal");
container.bind<OrderService>(APP_TYPES.OrderService).to(OrderService);
// Scenario 1: Default (Stripe)
const orderServiceDefault = container.get<OrderService>(APP_TYPES.OrderService);
orderServiceDefault.placeOrder("ORD001", 100, "stripe");
// Scenario 2: Request PayPal specifically
const orderServicePayPal = container.getNamed<OrderService>(APP_TYPES.OrderService, "paymentMethod", "paypal");
// This approach for conditional binding requires the consumer to know about the tag,
// or more commonly, the tag is applied to the consumer's dependency directly.
// A more direct way to get the PayPal processor for OrderService would be:
// Re-binding for demonstration (in a real app, you'd configure this once)
const containerForPayPal = new Container();
containerForPayPal.bind<IPaymentProcessor>(APP_TYPES.PaymentProcessor).to(StripePaymentProcessor);
containerForPayPal.bind<IPaymentProcessor>(APP_TYPES.PaymentProcessor)
.to(PayPalPaymentProcessor)
.when((request: interfaces.Request) => {
// A more advanced rule, e.g., inspect a request-scoped context
return request.parentRequest?.serviceIdentifier === APP_TYPES.OrderService && request.parentRequest.target.name === "paypal";
});
// For simplicity in direct consumption, you might define named bindings for processors
container.bind<IPaymentProcessor>("StripeProcessor").to(StripePaymentProcessor);
container.bind<IPaymentProcessor>("PayPalProcessor").to(PayPalPaymentProcessor);
// If OrderService needs to choose based on its own logic, it would @inject all processors and select
// Or if the *consumer* of OrderService determines the payment method:
const orderContainer = new Container();
orderContainer.bind<IPaymentProcessor>(APP_TYPES.PaymentProcessor).to(StripePaymentProcessor).whenTargetNamed("stripe");
orderContainer.bind<IPaymentProcessor>(APP_TYPES.PaymentProcessor).to(PayPalPaymentProcessor).whenTargetNamed("paypal");
@injectable()
class SmartOrderService {
constructor(
@inject(APP_TYPES.PaymentProcessor) @named("stripe") private stripeProcessor: IPaymentProcessor,
@inject(APP_TYPES.PaymentProcessor) @named("paypal") private paypalProcessor: IPaymentProcessor
) {}
async placeOrder(orderId: string, amount: number, method: 'stripe' | 'paypal'): Promise<boolean> {
console.log(`SmartOrderService placing order ${orderId} for ${amount} via ${method}...`);
if (method === 'stripe') {
return this.stripeProcessor.processPayment(amount);
} else if (method === 'paypal') {
return this.paypalProcessor.processPayment(amount);
}
return false;
}
}
orderContainer.bind<SmartOrderService>(APP_TYPES.OrderService).to(SmartOrderService);
const smartOrderService = orderContainer.get<SmartOrderService>(APP_TYPES.OrderService);
smartOrderService.placeOrder("SMART-001", 150, "paypal");
smartOrderService.placeOrder("SMART-002", 250, "stripe");
ããã¯ãInversifyJSãããã«æè»ã§åå®å šã§ãããã瀺ããŠãããè€éãªäŸåæ§ã°ã©ããæç¢ºãªæå³ããã£ãŠç®¡çã§ããããšãå¯èœã«ããŸããããã¯ãå€§èŠæš¡ã§ã°ããŒãã«ã«ã¢ã¯ã»ã¹å¯èœãªã¢ããªã±ãŒã·ã§ã³ã«ãšã£ãŠäžå¯æ¬ ãªç¹æ§ã§ãã
TypeDI
TypeDIã¯ãããäžã€ã®åªããTypeScriptãã¡ãŒã¹ãã®DIãœãªã¥ãŒã·ã§ã³ã§ããã·ã³ãã«ããšæå°éã®ãã€ã©ãŒãã¬ãŒãã«çŠç¹ãåœãŠãŠãããåºæ¬çãªãŠãŒã¹ã±ãŒã¹ã§ã¯InversifyJSãããå°ãªãèšå®æé ã§æžãããšãå€ãã§ããããã`reflect-metadata`ã«å€§ããäŸåããŠããŸãã
äž»èŠæ©èœïŒ
- æå°éã®èšå®ïŒèšå®ãããèŠçŽãéèŠããŸãã`emitDecoratorMetadata`ãæå¹ã«ãªããšãå€ãã®åçŽãªã±ãŒã¹ã¯`@Service()`ãš`@Inject()`ã ãã§èšå®ã§ããŸãã
- ã°ããŒãã«ã³ã³ããïŒããã©ã«ãã®ã°ããŒãã«ã³ã³ãããæäŸããŸããããã¯å°èŠæš¡ãªã¢ããªã±ãŒã·ã§ã³ãè¿ éãªãããã¿ã€ãäœæã«ã¯äŸ¿å©ã§ãããå€§èŠæš¡ãªãããžã§ã¯ãã§ã¯æç€ºçãªã³ã³ããã®äœ¿çšãæšå¥šãããŸãã
- ãµãŒãã¹ãã³ã¬ãŒã¿ïŒ`@Service()`ãã³ã¬ãŒã¿ã¯ãã¯ã©ã¹ãã³ã³ããã«èªåçã«ç»é²ãããã®äŸåæ§ãåŠçããŸãã
- ããããã£ããã³ã³ã³ã¹ãã©ã¯ã¿ã€ã³ãžã§ã¯ã·ã§ã³ïŒäž¡æ¹ããµããŒãããŸãã
- ã©ã€ããµã€ã¯ã«ã¹ã³ãŒãïŒãã©ã³ãžã§ã³ããšã·ã³ã°ã«ãã³ããµããŒãããŸãã
TypeDIã®äŸïŒåºæ¬çãªäœ¿ç𿹿³
import { Service, Inject } from 'typedi';
import "reflect-metadata"; // Required for decorators
interface ICurrencyConverter {
convert(amount: number, from: string, to: string): number;
}
@Service()
class ExchangeRateConverter implements ICurrencyConverter {
private rates: { [key: string]: number } = {
"USD_EUR": 0.85,
"EUR_USD": 1.18,
"USD_GBP": 0.73,
"GBP_USD": 1.37,
};
convert(amount: number, from: string, to: string): number {
const rateKey = `${from}_${to}`;
if (this.rates[rateKey]) {
return amount * this.rates[rateKey];
}
console.warn(`No exchange rate found for ${rateKey}. Returning original amount.`);
return amount; // Or throw an error
}
}
@Service()
class FinancialService {
constructor(@Inject(() => ExchangeRateConverter) private currencyConverter: ICurrencyConverter) {}
calculateInternationalTransfer(amount: number, fromCurrency: string, toCurrency: string): number {
console.log(`Calculating transfer of ${amount} ${fromCurrency} to ${toCurrency}.`);
return this.currencyConverter.convert(amount, fromCurrency, toCurrency);
}
}
// Resolve from the global container
const financialService = FinancialService.prototype.constructor.length === 0 ? new FinancialService(new ExchangeRateConverter()) : Service.get(FinancialService); // Example for direct instantiation or container get
// More robust way to get from container if using actual service calls
import { Container } from 'typedi';
const financialServiceFromContainer = Container.get(FinancialService);
const convertedAmount = financialServiceFromContainer.calculateInternationalTransfer(100, "USD", "EUR");
console.log(`Converted amount: ${convertedAmount} EUR`);
TypeDIã®`@Service()`ãã³ã¬ãŒã¿ã¯åŒ·åã§ããã¯ã©ã¹ã«`@Service()`ã®ããŒã¯ãä»ãããšãã³ã³ããã«èªèº«ãç»é²ããŸããå¥ã®ã¯ã©ã¹ïŒ`FinancialService`ïŒã`@Inject()`ã䜿çšããŠäŸåæ§ã宣èšãããšãTypeDIã¯`reflect-metadata`ã䜿çšããŠ`currencyConverter`ã®åïŒãã®èšå®ã§ã¯`ExchangeRateConverter`ïŒãæ€åºããã€ã³ã¹ã¿ã³ã¹ãæ³šå ¥ããŸãã`@Inject`å ã®ãã¡ã¯ããªé¢æ°`() => ExchangeRateConverter`ã®äœ¿çšã¯ã埪ç°äŸåæ§ã®åé¡ãåé¿ããããç¹å®ã®ã·ããªãªã§æ£ããåãªãã¬ã¯ã·ã§ã³ãä¿èšŒãããããããã«æã å¿ èŠã§ãããŸããåãã€ã³ã¿ãŒãã§ãŒã¹ã§ããå Žåã«ãããã¯ãªãŒã³ãªäŸåæ§å®£èšãå¯èœã«ããŸãã
TypeDIã¯åºæ¬çãªã»ããã¢ããã§ã¯ããã·ã³ãã«ã«æãããããããŸããããããå€§èŠæš¡ã§è€éãªã¢ããªã±ãŒã·ã§ã³ã§ã¯ãããè¯ãå¶åŸ¡ãšãã¹ãå®¹ææ§ã®ããã«æç€ºçãªã³ã³ãã管çã奜ãŸããå Žåãããããããã®ã°ããŒãã«ã³ã³ããã®æå³åããçè§£ããŠãã ããã
ã°ããŒãã«ããŒã ã®ããã®é«åºŠãªæŠå¿µãšãã¹ããã©ã¯ãã£ã¹
ç¹ã«ã°ããŒãã«éçºã®æèã§ãIoCã³ã³ãããçšããTypeScript DIãçã«ç¿åŸããã«ã¯ã以äžã®é«åºŠãªæŠå¿µãšãã¹ããã©ã¯ãã£ã¹ãèæ ®ããŠãã ããïŒ
1. ã©ã€ããµã€ã¯ã«ãšã¹ã³ãŒãïŒã·ã³ã°ã«ãã³ããã©ã³ãžã§ã³ãããªã¯ãšã¹ãïŒ
äŸåæ§ã®ã©ã€ããµã€ã¯ã«ç®¡çã¯ãããã©ãŒãã³ã¹ããªãœãŒã¹ç®¡çãããã³æ£ç¢ºãã«ãšã£ãŠæ¥µããŠéèŠã§ããIoCã³ã³ããã¯éåžžã以äžãæäŸããŸãïŒ
- ãã©ã³ãžã§ã³ãïŒãŸãã¯ã¹ã³ãŒãïŒïŒäŸåæ§ã®æ°ããã€ã³ã¹ã¿ã³ã¹ã¯ãèŠæ±ããããã³ã«äœæãããŸããã¹ããŒããã«ãªãµãŒãã¹ãã¹ã¬ããã»ãŒãã§ã¯ãªãã³ã³ããŒãã³ãã«æé©ã§ãã
- ã·ã³ã°ã«ãã³ïŒäŸåæ§ã®ã€ã³ã¹ã¿ã³ã¹ã¯ãã¢ããªã±ãŒã·ã§ã³ã®ã©ã€ãã¿ã€ã ïŒãŸãã¯ã³ã³ããã®ã©ã€ãã¿ã€ã ïŒå šäœã§1ã€ã ãäœæãããŸãããã®ã€ã³ã¹ã¿ã³ã¹ã¯ãèŠæ±ããããã³ã«åå©çšãããŸããã¹ããŒãã¬ã¹ãªãµãŒãã¹ãèšå®ãªããžã§ã¯ãããŸãã¯ããŒã¿ããŒã¹æ¥ç¶ããŒã«ã®ãããªé«äŸ¡ãªãªãœãŒã¹ã«æé©ã§ãã
- ãªã¯ãšã¹ãã¹ã³ãŒãïŒïŒãŠã§ããã¬ãŒã ã¯ãŒã¯ã§äžè¬çïŒçä¿¡HTTPãªã¯ãšã¹ãããšã«æ°ããã€ã³ã¹ã¿ã³ã¹ãäœæãããŸãããã®ã€ã³ã¹ã¿ã³ã¹ã¯ããã®ç¹å®ã®ãªã¯ãšã¹ãã®åŠçå šäœã§åå©çšãããŸããããã«ããããããŠãŒã¶ãŒã®ãªã¯ãšã¹ãã®ããŒã¿ãå¥ã®ãŠãŒã¶ãŒã®ãªã¯ãšã¹ãã«æ··å ¥ããã®ãé²ããŸãã
æ£ããã¹ã³ãŒããéžæããããšã¯éåžžã«éèŠã§ããã°ããŒãã«ããŒã ã¯ãäºæãã¬åäœããªãœãŒã¹æ¯æžãé²ãããã«ããããã®èŠçŽã«ã€ããŠåæããå¿ èŠããããŸãã
2. éåæäŸåæ§è§£æ±º
çŸä»£ã®ã¢ããªã±ãŒã·ã§ã³ã¯ãåæåã®ããã«éåææäœïŒäŸïŒããŒã¿ããŒã¹ãžã®æ¥ç¶ãåæèšå®ã®ãã§ããïŒã«äŸåããããšããããããŸããäžéšã®IoCã³ã³ããã¯éåæè§£æ±ºããµããŒãããŠãããäŸåæ§ãæ³šå ¥åã«`await`ããããšãã§ããŸãã
// Conceptual example with async binding
container.bind<IDatabaseClient>(TYPES.DatabaseClient)
.toDynamicValue(async () => {
const client = new DatabaseClient();
await client.connect(); // Asynchronous initialization
return client;
})
.inSingletonScope();
3. ãããã€ããã¡ã¯ããª
äŸåæ§ã®ã€ã³ã¹ã¿ã³ã¹ãæ¡ä»¶ä»ãã§ããŸãã¯æ¶è²»æç¹ã§ããäžæãªãã©ã¡ãŒã¿ã䜿çšããŠäœæããå¿ èŠãããå ŽåããããŸãããããã€ããã¡ã¯ããªã䜿çšãããšãåŒã³åºããããšãã«äŸåæ§ãäœæãã颿°ãæ³šå ¥ã§ããŸãã
import { Container, injectable, inject } from "inversify";
import "reflect-metadata";
interface IReportGenerator {
generateReport(data: any): string;
}
@injectable()
class PdfReportGenerator implements IReportGenerator {
generateReport(data: any): string {
return `PDF Report for: ${JSON.stringify(data)}`;
}
}
@injectable()
class CsvReportGenerator implements IReportGenerator {
generateReport(data: any): string {
return `CSV Report for: ${Object.keys(data).join(',')}\n${Object.values(data).join(',')}`;
}
}
const REPORT_TYPES = {
Pdf: Symbol.for("PdfReportGenerator"),
Csv: Symbol.for("CsvReportGenerator"),
ReportService: Symbol.for("ReportService"),
};
// The ReportService will depend on a factory function
interface ReportGeneratorFactory {
(format: 'pdf' | 'csv'): IReportGenerator;
}
@injectable()
class ReportService {
constructor(
@inject(REPORT_TYPES.ReportGeneratorFactory) private reportGeneratorFactory: ReportGeneratorFactory
) {}
createReport(format: 'pdf' | 'csv', data: any): string {
const generator = this.reportGeneratorFactory(format);
return generator.generateReport(data);
}
}
const reportContainer = new Container();
// Bind specific report generators
reportContainer.bind<IReportGenerator>(REPORT_TYPES.Pdf).to(PdfReportGenerator);
reportContainer.bind<IReportGenerator>(REPORT_TYPES.Csv).to(CsvReportGenerator);
// Bind the factory function
reportContainer.bind<ReportGeneratorFactory>(REPORT_TYPES.ReportGeneratorFactory)
.toFactory<IReportGenerator>((context: interfaces.Context) => {
return (format: 'pdf' | 'csv') => {
if (format === 'pdf') {
return context.container.get<IReportGenerator>(REPORT_TYPES.Pdf);
} else if (format === 'csv') {
return context.container.get<IReportGenerator>(REPORT_TYPES.Csv);
}
throw new Error(`Unknown report format: ${format}`);
};
});
reportContainer.bind<ReportService>(REPORT_TYPES.ReportService).to(ReportService);
const reportService = reportContainer.get<ReportService>(REPORT_TYPES.ReportService);
const salesData = { region: "EMEA", totalSales: 150000, month: "January" };
console.log(reportService.createReport("pdf", salesData));
console.log(reportService.createReport("csv", salesData));
ãã®ãã¿ãŒã³ã¯ãäŸåæ§ã®æ£ç¢ºãªå®è£ ãåçãªæ¡ä»¶ã«åºã¥ããŠå®è¡æã«æ±ºå®ãããå¿ èŠãããå Žåã«éåžžã«äŸ¡å€ãããããã®ãããªæè»æ§ããã£ãŠãåå®å šæ§ãä¿èšŒããŸãã
4. DIãçšãããã¹ãæŠç¥
DIã®äž»èŠãªæšé²èŠå ã®1ã€ã¯ãã¹ãå®¹ææ§ã§ãããã¹ããã¬ãŒã ã¯ãŒã¯ãéžæããIoCã³ã³ãããšç°¡åã«çµ±åã§ããäŸåæ§ã广çã«ã¢ãã¯ãŸãã¯ã¹ã¿ãã§ããããšã確èªããŠãã ãããåäœãã¹ãã®å Žåããã¹ã察象ã®ã³ã³ããŒãã³ãã«ã¢ãã¯ãªããžã§ã¯ããçŽæ¥æ³šå ¥ããã³ã³ãããå®å šã«ãã€ãã¹ããããšããããããŸããçµ±åãã¹ãã®å Žåããã¹ãåºæã®å®è£ ã§ã³ã³ãããæ§æããããšãã§ããŸãã
5. ãšã©ãŒåŠçãšãããã°
äŸåæ§è§£æ±ºã倱æããå ŽåïŒäŸïŒãã€ã³ãã£ã³ã°ãäžè¶³ããŠããã埪ç°äŸåæ§ãååšããïŒãåªããIoCã³ã³ããã¯æç¢ºãªãšã©ãŒã¡ãã»ãŒãžãæäŸããŸããéžæããã³ã³ããããããã®åé¡ãã©ã®ããã«å ±åããããçè§£ããŠãã ãããTypeScriptã®ã³ã³ãã€ã«æãã§ãã¯ã¯ãããã®ãšã©ãŒãå€§å¹ ã«åæžããŸãããå®è¡æã®èšå®ãã¹ã¯äŸç¶ãšããŠçºçããå¯èœæ§ããããŸãã
6. ããã©ãŒãã³ã¹ã«é¢ããèæ ®äºé
IoCã³ã³ããã¯éçºãç°¡çŽ åããŸããããªãã¬ã¯ã·ã§ã³ãšãªããžã§ã¯ãã°ã©ãäœæã«é¢é£ããããããªå®è¡æãªãŒããŒãããããããŸããã»ãšãã©ã®ã¢ããªã±ãŒã·ã§ã³ã§ã¯ããã®ãªãŒããŒãããã¯ç¡èŠã§ããŸããããããæ¥µããŠããã©ãŒãã³ã¹ã«ææãªã·ããªãªã§ã¯ãã¡ãªãããæœåšçãªåœ±é¿ãäžåããã©ãããæ éã«æ€èšããŠãã ãããææ°ã®JITã³ã³ãã€ã©ãšæé©åãããã³ã³ããå®è£ ã¯ããã®æžå¿µã®å€§éšåã軜æžããŸãã
ã°ããŒãã«ãããžã§ã¯ãã«é©ããIoCã³ã³ããã®éžæ
ç¹ã«ã°ããŒãã«ãªãªãŒãã£ãšã³ã¹ãšåæ£éçºããŒã ã察象ãšããTypeScriptãããžã§ã¯ãã§IoCã³ã³ãããéžæããéã«ã¯ã以äžã®èŠçŽ ãèæ ®ããŠãã ããïŒ
- åå®å šæ§æ©èœïŒ`reflect-metadata`ã广çã«æŽ»çšããŠããŸããïŒã³ã³ãã€ã«æã«ãªãã¹ãåå®å šæ§ã匷å¶ããŸããïŒ
- æç床ãšã³ãã¥ããã£ãµããŒãïŒæŽ»çºãªéçºãšåŒ·åãªã³ãã¥ããã£ãæã€ç¢ºç«ãããã©ã€ãã©ãªã¯ãããè¯ãããã¥ã¡ã³ãããã°ä¿®æ£ãé·æçãªå®è¡å¯èœæ§ãä¿èšŒããŸãã
- æè»æ§ïŒæ§ã ãªãã€ã³ãã£ã³ã°ã·ããªãªïŒæ¡ä»¶ä»ããååä»ããã¿ã°ä»ãïŒãåŠçã§ããŸããïŒç°ãªãã©ã€ããµã€ã¯ã«ããµããŒãããŠããŸããïŒ
- 䜿ãããããšåŠç¿æ²ç·ïŒå€æ§ãªæè²çèæ¯ãæã€å¯èœæ§ã®ããæ°ããããŒã ã¡ã³ããŒããã©ã®ãããæ©ãç¿çã§ããŸããïŒ
- ãã³ãã«ãµã€ãºïŒããã³ããšã³ããŸãã¯ãµãŒããŒã¬ã¹ã¢ããªã±ãŒã·ã§ã³ã®å Žåãã©ã€ãã©ãªã®ãããããªã³ããèŠå ãšãªãããšããããŸãã
- ãã¬ãŒã ã¯ãŒã¯ãšã®çµ±åïŒNestJSïŒç¬èªã®DIã·ã¹ãã ãæã£ãŠããŸãïŒãExpressãAngularãªã©ã®äººæ°ã®ãããã¬ãŒã ã¯ãŒã¯ãšããŸãçµ±åã§ããŸããïŒ
InversifyJSãšTypeDIã¯ã©ã¡ããTypeScriptã«ãšã£ãŠåªããéžæè¢ã§ãããããããã«åŒ·ã¿ããããŸããè€éãªäŸåæ§ã°ã©ããæã¡ãæç€ºçãªèšå®ãéèŠããå ç¢ãªãšã³ã¿ãŒãã©ã€ãºã¢ããªã±ãŒã·ã§ã³ã«ã¯ãInversifyJSããããã现ããªå¶åŸ¡ãæäŸããããšããããããŸããèŠçŽãšæå°éã®ãã€ã©ãŒãã¬ãŒããéèŠãããããžã§ã¯ãã«ã¯ãTypeDIãéåžžã«é åçã§ãã
çµè«ïŒã¬ãžãªãšã³ãã§åå®å šãªã°ããŒãã«ã¢ããªã±ãŒã·ã§ã³ã®æ§ç¯
TypeScriptã®éçåä»ããšãIoCã³ã³ãããçšããé©åã«å®è£ ãããäŸåæ§æ³šå ¥æŠç¥ã®çµã¿åããã¯ãã¬ãžãªãšã³ãã§ä¿å®å¯èœããã€é«ããã¹ãå®¹ææ§ãæã€ã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ããããã®åŒ·åãªåºç€ã圢æããŸããã°ããŒãã«éçºããŒã ã«ãšã£ãŠããã®ã¢ãããŒãã¯åãªãæè¡çå奜ã§ã¯ãªããæŠç¥çãªå¿ é äºé ã§ãã
äŸåæ§æ³šå ¥ã¬ãã«ã§åå®å šæ§ã匷å¶ããããšã§ãéçºè ã¯ãšã©ãŒãæ©æã«æ€åºããèªä¿¡ãæã£ãŠãªãã¡ã¯ã¿ãªã³ã°ããå®è¡æé害ãå°ãªãé«å質ãªã³ãŒããçæã§ããããã«ãªããŸããããã«ããããããã°æéã®ççž®ãéçºãµã€ã¯ã«ã®é«éåããããŠæçµçã«ã¯äžçäžã®ãŠãŒã¶ãŒã«ãšã£ãŠããå®å®ããå ç¢ãªè£œåãžãšã€ãªãããŸãã
ãããã®ãã¿ãŒã³ãšããŒã«ãåãå ¥ãããã®ãã¥ã¢ã³ã¹ãçè§£ããç±å¿ã«é©çšããŠãã ãããããªãã®ã³ãŒãã¯ããã¯ãªãŒã³ã«ãªããããŒã ã¯ããçç£çã«ãªããããªãã®ã¢ããªã±ãŒã·ã§ã³ã¯çŸä»£ã®ã°ããŒãã«ãœãããŠã§ã¢ã©ã³ãã¹ã±ãŒãã®è€éããšã¹ã±ãŒã«ã«å¯Ÿå¿ããããã«ããè¯ãæºåãããã§ãããã
TypeScriptã®äŸåæ§æ³šå ¥ã«é¢ããããªãã®çµéšã¯ãããã§ããïŒãæèŠã奜ã¿ã®IoCã³ã³ããã以äžã®ã³ã¡ã³ãæ¬ã§å ±æããŠãã ããïŒ